/**
 *
 */
package coms6111.astbbw.dataminer;

import java.io.*;
import java.util.*;

/**
 * @author bbw2108
 *
 */
public class GlimpseBitIndexed extends NaiveGlimpseMine {
	private final static String QUERY_ARGS = "-ciwNyg";
	
	
	/**
	 * @param datasetName
	 */
	public GlimpseBitIndexed(String datasetName) {
		super(datasetName);
	}

	public static GlimpseBitIndexed unDump(File dumpFile) throws FileNotFoundException, IOException {
		BufferedReader b = new BufferedReader(new InputStreamReader(new FileInputStream(dumpFile)));
		String line = b.readLine();
		String fields[] = line.split(" ");
		GlimpseBitIndexed undumped = new GlimpseBitIndexed(fields[0]);
		undumped.totalDocs = Integer.parseInt(fields[1]);
		while (null != (line = b.readLine())) {
			undumped.addItemSet(line);
		}
		b.close();
		return undumped;
	}

	private void addItemSet(String dumpedLine) {
		/* NOTE WELL: this will only work on singleton sets */
		String fields[] = dumpedLine.split(" ");
		short docIds[] = new short[fields.length - 1];
		String terms[] = { fields[0].substring(0, fields[0].indexOf(':')) };
		for (int i = 1; i < fields.length; i++) docIds[i - 1] = Short.parseShort(fields[i]);
		this.singletonSets.add(new ItemSetInt(terms,docIds));
	}
	/* (non-Javadoc)
	 * @see coms6111.astbbw.dataminer.NaiveGlimpseMine#createDFStructure()
	 */


	@Override
	protected void indexTerm(String word) {
		ArrayList<Integer> docList = new ArrayList<Integer>();
		String cmdline[] = { QUERY, 
				     "-H", this.tmpdirPath, 
				     "-T", this.tmpdirPath, 
				     QUERY_ARGS, word };
		String termList[] = {word};
		minValue=(1/totalDocs);
		Process p = null;
		try {
			p = Runtime.getRuntime().exec(cmdline);
			BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
			String line = null;
			while (null != (line = in.readLine())) {
				int found = Integer.parseInt(line);
				docList.add(found);
			}
			int status = p.waitFor();
			if (2 == status) {
				in = new BufferedReader(new InputStreamReader(p.getErrorStream()));
				logger.fatal("Glimpse error value");
				while (null != (line = in.readLine())) logger.debug(line);
				throw new RuntimeException("wha?");
			}

			if (docList.size() > 0) {
				ItemBitSet newset = new ItemSetInt(termList, docList.toArray(new Integer[0]));
				synchronized (singletonSets) {

					this.singletonSets.add(newset);
				}
			} else {
				synchronized (termsToDelete) {
					termsToDelete.add(word);
				}
			}
		} catch (IOException e) {
			logger.error("IOException while running glimpse for '" + word
					+ "': " + e.getLocalizedMessage());
		} catch (InterruptedException e) {
			logger.fatal("Somehow interrupted while waiting for glimpse",e);
			System.exit(1);
		} catch (NumberFormatException e) {
			logger.error("Error parsing glimpse output for " + word,e);
		} finally {
			try {
				p.getErrorStream().close();
				p.getInputStream().close();
				p.getOutputStream().close();
				p.destroy();
			} catch (Exception e) {
				logger.error("Error finalizing glimpse process for " + word,e);
			}
		}
	}

	
	
	
	class ItemBitSet extends ItemSet {
		protected BitSet transactionList;

		/* (non-Javadoc)
		 * @see coms6111.astbbw.dataminer.DataMine.ItemSet#lineSerialize()
		 */
		@Override
		protected StringBuffer lineSerialize() {
			StringBuffer b = super.lineSerialize();
			b = appendDocIds(b);
			return b;
		}

		protected StringBuffer appendDocIds(StringBuffer in) {
			BitSet set = this.transactionList;
			for (int i = set.nextSetBit(0); i >= 0; i = set.nextSetBit(i+1)) {
				in.append(' ');
				in.append( Integer.toString(i) );
			}
			return in;
		}

		public ItemBitSet(String[] items, double support) {
			super(items, support);
		}

		public ItemBitSet(String word, double d) {
			super(word, d);
		}

		public ItemBitSet(String[] items, Integer docids[]) {
			super(items, (float) docids.length / GlimpseBitIndexed.this.totalDocs);
			transactionList = new BitSet();
			for (int doc : docids) transactionList.set(doc);
		}
		public ItemBitSet(String[] items, BitSet index) {
			super(items, (double) index.cardinality()/ GlimpseBitIndexed.this.totalDocs);
			this.transactionList = index;
		}

		public ItemSet intersect(ItemSet other_in, boolean finalRound) {
			ItemBitSet other;
			if ( !(other_in instanceof ItemBitSet)) { // unnecessary type-safety?
				GlimpseBitIndexed.this.logger.warn("Found that branch that Ben thought wouldn't happen");
				return super.intersect(other_in, finalRound);
			} else {
				other = (ItemBitSet) other_in;
			}
			String[] terms = intersectionTerms(other);
			String key = makeKey(terms);
			if (GlimpseBitIndexed.this.itemCache.containsKey(key)) {
				return GlimpseBitIndexed.this.itemCache.get(key);
			} else {
				double newSupport;
				BitSet newBits = (BitSet) this.transactionList.clone();
				newBits.and(other.transactionList);
				newSupport = (double) newBits.cardinality() / totalDocs ;
				if (newSupport < minSupport) return null;
				return finalRound
				? new ItemSet(terms, newSupport)
				: new ItemBitSet(terms, newBits);
			}
		}

	}


	class ItemSetInt extends ItemBitSet {
		protected short[] transList;


		/* (non-Javadoc)
		 * @see coms6111.astbbw.dataminer.GlimpseBitIndexed.ItemBitSet#appendDocIds(java.lang.StringBuffer)
		 */
		@Override
		protected StringBuffer appendDocIds(StringBuffer in) {
			for (int i : this.transList) {
				in.append(' ');
				in.append( Integer.toString(i) );
			}
			return in;
		}

		public ItemSetInt(String[] items, double support) {
			super(items, support);
			throw new RuntimeException("This constructor should not be called");
		}

		public ItemSetInt(String word, double d) {
			super(word, d);
			throw new RuntimeException("This constructor should not be called");
		}

		public ItemSetInt(String[] items, Integer docids[]) {
			super(items, (float) docids.length / GlimpseBitIndexed.this.totalDocs);

			transList = new short[docids.length];
			for (int i=0 ;i< docids.length;i++) transList[i]= docids[i].shortValue();
		}

		/* this is private: we don't copy the array, because we know it's already
		 * copied, but we don't want just anybody doing that.
		 */
		private ItemSetInt(String[] items, short docids[]) {
			super(items, (float) docids.length / GlimpseBitIndexed.this.totalDocs);
			transList = docids;
		}

		public ItemSet intersect(ItemSet other_in,  boolean finalRound) {
			ItemSetInt other;
			if ( !(other_in instanceof ItemSetInt)) { // unnecessary type-safety?
				GlimpseBitIndexed.this.logger.warn("Found that branch that Ben thought wouldn't happen");
				return super.intersect(other_in, finalRound);
			} else {
				other = (ItemSetInt) other_in;
			}
			
			short temp[] = new short[transList.length];
			int i = this.transList.length - 1;
			int j = other.transList.length - 1;
			int found = 0;
			int needed = minDocs - 1;
			while( 0 <= i  && 0 <= j )
			{
				short my_doc = this.transList[i];
				short other_doc = other.transList[j];
				if(my_doc == other_doc){
					temp[found++] = my_doc;
					needed--;
					i--;
					j--;
				}
				else if(my_doc < other_doc) j--;
				else i--;
				if ( i < needed || j < needed) break; /* bail out! */
			}
			if (found < minDocs) return null;
			/* oh right, the reversal... */
			short fixed[] = new short[found];
			if (found > 0)  {
				for (int k = 0; k <= found/2; k++) {
					int swapidx = found - k -1;
					fixed[k] = temp[swapidx];
					fixed[swapidx] = temp[k];
				}
			}
			return new ItemSetInt(intersectionTerms(other), fixed);

		}

	}

	

}
